home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / WWW / Perl_WWW_Utilities / mailto.pl < prev    next >
Encoding:
Perl Script  |  1996-04-24  |  15.3 KB  |  513 lines

  1. #!/usr/local/bin/perl
  2. #
  3. # Doug's WWW Mail Gateway 2.2
  4. # 5/95
  5. # All material here is Copyright 1995 Doug Stevenson.
  6. #
  7. # Use this script as a front end to mail in your HTML.  Not every browser
  8. # supports the mailto: URLs, so this is the next best thing.  If you
  9. # use this script, please leave credits to myself intact!  :)  You can
  10. # modify it all you want, though.
  11. #
  12. # Documentation at:
  13. #    http://www-bprc.mps.ohio-state.edu/mailto/mailto_info.html
  14. #
  15. # Configurable items are just below.  Also pay special attention to
  16. # GET method arguments that this script accepts to specify defaults
  17. # for some fields.
  18. #
  19. # I didn't exactly follow the RFCs on mail headers when I wrote this,
  20. # so please send all flames my way if it breaks your mail client!!
  21. # Also, you'll need cgi-lib.pl for the GET and POST parsing.  I use
  22. # version 1.7.
  23. #
  24. # Requires cgi-lib.pl which can be found at
  25. #    http://www.bio.cam.ac.uk/web/form.html
  26. #
  27. # PLEASE: Use this script freely, but leave credits to myself!!  It's
  28. #         common decency!
  29. #
  30. ########
  31. #
  32. # Changes from 1.1 to 1.2:
  33. #
  34. # A common modification to the script for others to make was to allow
  35. # only a certain few mail addresses to be sent to.  I changed the WWW
  36. # Mail Gateway to allow only those mail addresses in the list @addrs
  37. # to be mailed to - they are placed in a HTML <SELECT> list, with either
  38. # the selected option being either the first one or the one that matches
  39. # the "to" CGI variable.  Thanks to Mathias Koerber
  40. # <Mathias.Koerber@swi.com.sg> for this suggestion.
  41. #
  42. # Also made one minor fix.
  43. #
  44. ########
  45. #
  46. # Changes from 1.2 to 1.3:
  47. #
  48. # Enhancing the enhancements from 1.2.  You can now specify a real name
  49. # or some kind of identifier to go with the real mail address.  This
  50. # infomation gets put in the %addrs associative array, either explicitly
  51. # defined, or read from a file.  Read the information HTML for instructions
  52. # on how to set this up.  Also, real mail addresses may hidden from the
  53. # user.  Undefine or set to zero the variable $expose_address below.
  54. #
  55. ########
  56. #
  57. # Changes from 1.3 to 1.4
  58. #
  59. # The next URL to be fetched after the mail is sent can be specified with
  60. # the cgi varaible 'nexturl'.
  61. #
  62. # Fixed some stupid HTML mistake.
  63. #
  64. # Force user to enter something for the username on 'Your Email:' tag,
  65. # if identd didn't get a username.
  66. #
  67. # Added Cc: field, only when %addrs is not being used.
  68. #
  69. ########
  70. #
  71. # Quickie patch to 1.41
  72. #
  73. # Added <PRE>formatted part to header entry to make it look nice and fixed a
  74. # typo.
  75. #
  76. ########
  77. #
  78. # Version 2.0 changes
  79. #
  80. # ALL cgi varaibles (except those reserved for mail info) are logged
  81. # at then end of the mail received.  You can put forms, hidden data,
  82. # or whatever you want, and the info for each variable will get logged.
  83. #
  84. # Cleaned up a lot of spare code.
  85. #
  86. # IP addresses are now correctly logged instead of just hostnames.
  87. #
  88. # Made source retrieval optional.
  89. #
  90. ########
  91. #
  92. # Changes from 2.0 to 2.1
  93. #
  94. # Fixed stupid HTML error for an obscure case.  Probably never noticed.
  95. #
  96. # Reported keys are no longer reported in an apparently random order; they
  97. # are listed in the order they were received.  That was a function of perl
  98. # hashes...changed to a list operation instead.
  99. #
  100. ########
  101. #
  102. # Changes from 2.1 to 2.2
  103. #
  104. # Added all kinds of robust error checking and reporting.  Should be
  105. # easier to diagnose problems from the user end.
  106. #
  107. # New suggested sendmail flag -oi to keep sendmail from ending mail
  108. # input on line containing . only.
  109. #
  110. # Added support for setting the "real" From address in the first line
  111. # of the mail header using the -f sendmail switch.  This may or may not
  112. # be what you want, depending on the application of the script.  This is
  113. # useful for listservers that use that information for identification
  114. # purposes or whatever.  This is NOT useful if you're concerned about
  115. # the security of your script for public usage.  Your mileage will vary,
  116. # please read the sendmail manual about the -f switch.
  117. #    Thanks to Jeff Lawrence (jlaw@irus.rri.uwo.ca) for figuring this
  118. #    one out.
  119. #
  120. ########
  121. #
  122. # Doug Stevenson
  123. # doug+@osu.edu
  124.  
  125. ######################
  126. # Configurable options
  127. ######################
  128.  
  129. # whether or not to actually allow mail to be sent -- for testing purposes
  130. $active = 1;
  131.  
  132. # Logging flag.  Logs on POST method when mail is sent.
  133. $logging = 1;
  134. $logfile = '/usr/local/WWW/etc/mailto_log';
  135.  
  136. # Physical script location.  Define ONLY if you wish to make your version
  137. # of this source code available with GET method and the suffix '?source'
  138. # on the url.
  139. $script_loc = '/usr/local/WWW/cgi-bin/mailto.pl';
  140.  
  141. # physical location of your cgi-lib.pl
  142. $cgi_lib = '/usr/local/WWW/cgi-bin/cgi-lib.pl';
  143.  
  144. # http script location
  145. $script_http = 'http://www-bprc.mps.ohio-state.edu/cgi-bin/mailto.pl';
  146.  
  147. # Path to sendmail and its flags.  Use the first commented version and
  148. # define $listserver = 1if you want the gateway to be used for listserver
  149. # subscriptions -- the -f switch might be neccesary to get this to work
  150. # correctly.
  151. #
  152. # sendmail options:
  153. #    -n  no aliasing
  154. #    -t  read message for "To:"
  155. #    -oi don't terminate message on line containing '.' alone
  156. #$sendmail = "/usr/lib/sendmail -t -n -oi -f";  $listserver = 1;
  157. $sendmail = "/usr/lib/sendmail -t -n -oi";
  158.  
  159. # set to 1 if you want the real addresses to be exposed from %addrs
  160. #$expose_address = 1;
  161.  
  162. # Uncomment one of the below chunks of code to implement restricted mail
  163.  
  164. # List of address to allow ONLY - gets put in a HTML SELECT type menu.
  165. #
  166. #%addrs = ("Doug - main address", "doug+@osu.edu",
  167. #      "Doug at BPRC", "doug@polarmet1.mps.ohio-state.edu",
  168. #      "Doug at CIS", "stevenso@cis.ohio-state.edu",
  169. #      "Doug at the calc lab", "dstevens@mathserver.mps.ohio-state.edu",
  170. #      "Doug at Magnus", "dmsteven@magnus.acs.ohio-state.edu");
  171.  
  172. # If you don't want the actual mail addresses to be visible by people
  173. # who view source, or you don't want to mess with the source, read them
  174. # from $mailto_addrs:
  175. #
  176. #$mailto_addrs = '/usr/local/WWW/etc/mailto_addrs';
  177. #open(ADDRS,$mailto_addrs);
  178. #while(<ADDRS>) {
  179. #    ($name,$address) = /^(.+)[ \t]+([^ ]+)\n$/;
  180. #    $name =~ s/[ \t]*$//;
  181. #    $addrs{$name} = $address;
  182. #}
  183.  
  184. # version
  185. $version = '2.2';
  186.  
  187. #############################
  188. # end of configurable options
  189. #############################
  190.  
  191.  
  192. ##########################
  193. # source is self-contained
  194. ##########################
  195.  
  196. if ($ENV{'QUERY_STRING'} eq 'source' && defined($script_loc)) {
  197.     print "Content-Type: text/plain\n\n";
  198.     open(SOURCE, $script_loc) ||
  199.     &InternalError('Could not open file containing source code');
  200.     print <SOURCE>;
  201.     close(SOURCE);
  202.     exit(0);
  203. }
  204.  
  205. require $cgi_lib;
  206. &ReadParse();
  207.  
  208. #########################################################################
  209. # method GET implies that we want to be given a FORM to fill out for mail
  210. #########################################################################
  211.  
  212. if ($ENV{'REQUEST_METHOD'} eq 'GET') {
  213.     # try to get as much info as possible for fields
  214.     # To:     comes from $in{'to'}
  215.     # Cc:     comes from $in{'cc'}
  216.     # From:   comes from REMOTE_IDENT@REMOTE_HOST || $in{'from'} || REMOTE_USER
  217.     # Subject: comes from $in{'sub'}
  218.     # body comes from $in{'body'}
  219.  
  220.     $destaddr = $in{'to'};
  221.     $cc = $in{'cc'};
  222.     $subject = $in{'sub'};
  223.     $body = $in{'body'};
  224.     $nexturl = $in{'nexturl'};
  225.  
  226.     if ($in{'from'}) {
  227.     $fromaddr = $in{'from'};
  228.     }
  229.     # this is for NetScape pre-1.0 beta users - probably obsolete code
  230.     elsif ($ENV{'REMOTE_USER'}) {
  231.     $fromaddr = $ENV{'REMOTE_USER'};
  232.     }
  233.     # this is for Lynx users, or any HTTP/1.0 client giving From header info
  234.     elsif ($ENV{'HTTP_FROM'}) {
  235.     $fromaddr = $ENV{'HTTP_FROM'};
  236.     }
  237.     # if all else fails, make a guess
  238.     else {
  239.     $fromaddr = "$ENV{'REMOTE_IDENT'}\@$ENV{'REMOTE_HOST'}";
  240.     }
  241.  
  242.     # Convert multiple bodies (separated by \0 according to CGI spec)
  243.     # into one big body
  244.     $body =~ s/\0//;
  245.  
  246.     # Make a list of authorized addresses if %addrs exists.
  247.     if (%addrs) {
  248.     $selections = '<SELECT NAME="to">';
  249.     foreach (sort keys %addrs) {
  250.         if ($in{'to'} eq $addrs{$_}) {
  251.         $selections .= "<OPTION SELECTED>$_";
  252.         }
  253.         else {
  254.         $selections .= "<OPTION>$_";
  255.         }
  256.         if ($expose_address) {
  257.         $selections .= " <$addrs{$_}>";
  258.         }
  259.     }
  260.     $selections .= "</SELECT>\n";
  261.     }
  262.  
  263.     # give them the form
  264.     print &PrintHeader();
  265.     print <<EOH;
  266. <HTML><HEAD><TITLE>Doug\'s WWW Mail Gateway $version</TITLE></HEAD>
  267. <BODY><H1><IMG SRC="http://www-bprc.mps.ohio-state.edu/pics/mail2.gif" ALT="">
  268. The WWW Mail Gateway $version</H1>
  269.  
  270. <P>The <B>To</B>: field should contain the <B>full</B> E-mail address
  271. that you want to mail to.  The <B>Your Email</B>: field needs to
  272. contain your mail address so replies go to the right place.  Type your
  273. message into the text area below. If the <B>To</B>: field is invalid,
  274. or the mail bounces for some reason, you will receive notification
  275. if <B>Your Email</B>: is set correctly.  <I>If <B>Your Email</B>:
  276. is set incorrectly, all bounced mail will be sent to the bit bucket.</I></P>
  277.  
  278. <FORM ACTION="$script_http" METHOD=POST>
  279. EOH
  280.     ;
  281.     print "<P><PRE>        <B>To</B>: ";
  282.  
  283.     # give the selections if set, or INPUT if not
  284.     if ($selections) {
  285.     print $selections;
  286.     }
  287.     else {
  288.     print "<INPUT VALUE=\"$destaddr\" SIZE=40 NAME=\"to\">\n";
  289.     print "        <B>Cc</B>: <INPUT VALUE=\"$cc\" SIZE=40 NAME=\"cc\">\n";
  290.     }
  291.  
  292.     print <<EOH;
  293.  <B>Your Name</B>: <INPUT VALUE="$fromname" SIZE=40 NAME="name">
  294. <B>Your Email</B>: <INPUT VALUE="$fromaddr" SIZE=40 NAME="from">
  295.    <B>Subject</B>: <INPUT VALUE="$subject" SIZE=40 NAME="sub"></PRE>
  296. <INPUT TYPE="submit" VALUE="Send the mail">
  297. <INPUT TYPE="reset" VALUE="Start over"><BR>
  298. <TEXTAREA ROWS=20 COLS=60 NAME="body">$body</TEXTAREA><BR>
  299. <INPUT TYPE="submit" VALUE="Send the mail">
  300. <INPUT TYPE="reset" VALUE="Start over"><BR>
  301. <INPUT TYPE="hidden" NAME="nexturl" VALUE="$nexturl"></P>
  302. </FORM>
  303. <HR>
  304.  
  305. <H2>Information about the WWW Mail Gateway</H2>
  306. <H3><A HREF="http://www-bprc.mps.ohio-state.edu/mailto/mailto_info.html#about">
  307. About the WWW Mail Gateway</A></H3>
  308. <H3><A HREF="http://www-bprc.mps.ohio-state.edu/mailto/mailto_info.html#new">
  309. New in version $version</A></H3>
  310. <H3><A HREF="http://www-bprc.mps.ohio-state.edu/mailto/mailto_info.html#misuse">
  311. Please report misuse!</A></H3>
  312.  
  313. <HR>
  314. <ADDRESS><P><A HREF="/~doug/">Doug Stevenson: doug+\@osu.edu</A>
  315. </P></ADDRESS>
  316. </BODY></HTML>
  317. EOH
  318.     ;
  319. }
  320.  
  321. #########################################################################
  322. # Method POST implies that they already filled out the form and submitted
  323. # it, and now it is to be processed.
  324. #########################################################################
  325.  
  326. elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
  327.     # get all the variables in their respective places
  328.     $destaddr = $in{'to'};
  329.     $cc       = $in{'cc'};
  330.     $fromaddr = $in{'from'};
  331.     $fromname = $in{'name'};
  332.     $replyto  = $in{'from'};
  333.     $sender   = $in{'from'};
  334.     $errorsto = $in{'from'};
  335.     $subject  = $in{'sub'};
  336.     $body     = $in{'body'};
  337.     $nexturl  = $in{'nexturl'};
  338.     $realfrom = $ENV{'REMOTE_HOST'} ? $ENV{'REMOTE_HOST'}: $ENV{'REMOTE_ADDR'};
  339.  
  340.     # check to see if required inputs were filled - error if not
  341.     unless ($destaddr && $fromaddr && $body && ($fromaddr =~ /^.+\@.+/)) {
  342.     print <<EOH;
  343. Content-type: text/html
  344. Status: 400 Bad Request
  345.  
  346. <HTML><HEAD><TITLE>Mailto error</TITLE></HEAD>
  347. <BODY><H1>Mailto error</H1>
  348. <P>One or more of the following necessary pieces of information was missing
  349. from your mail submission:
  350. <UL>
  351. <LI><B>To</B>:, the full mail address you wish to send mail to</LI>
  352. <LI><B>Your Email</B>: your full email address</LI>
  353. <LI><B>Body</B>: the text you wish to send</LI>
  354. </UL>
  355. Please go back and fill in the missing information.</P></BODY></HTML>
  356. EOH
  357.     exit(0);
  358.     }
  359.  
  360.     # do some quick logging - you may opt to have more/different info written
  361.     if ($logging) {
  362.     open(MAILLOG,">>$logfile");
  363.     print MAILLOG "$realfrom\n";
  364.     close(MAILLOG);
  365.     }
  366.  
  367.     # Log every CGI variable except for the ones reserved for mail info.
  368.     # Valid vars go into @data.  Text output goes into $data and gets.
  369.     # appended to the end of the mail.
  370.     # First, get an ORDERED list of all cgi vars from @in to @keys
  371.     for (0 .. $#in) {
  372.     local($key) = split(/=/,$in[$_],2);
  373.     $key =~ s/\+/ /g;
  374.     $key =~ s/%(..)/pack("c",hex($1))/ge;
  375.     push(@keys,$key);
  376.     }
  377.     # Now weed out the ones we want
  378.     @reserved = ('to', 'cc', 'from', 'name', 'sub', 'body', 'nexturl');
  379.     local(%mark);
  380.     foreach (@reserved) { $mark{$_} = 1; }
  381.     @data = grep(!$mark{$_}, @keys);
  382.     foreach (@data) {
  383.     $data .= "$_ -> $in{$_}\n";
  384.     }
  385.  
  386.     # Convert multiple bodies (separated by \0 according to CGI spec)
  387.     # into one big body
  388.     $body =~ s/\0//;
  389.  
  390.     # now check to see if some joker changed the HTML to allow other
  391.     # mail addresses besides the ones in %addrs, if applicable
  392.     if (%addrs) {
  393.     if (!scalar(grep($_." <$addrs{$_}>" eq $destaddr ||
  394.              $destaddr eq $_, keys(%addrs)))) {
  395.         print &PrintHeader();
  396.         print <<EOH;
  397. <HTML><HEAD><TITLE>WWW Mail Gateway: Mail address not allowed</TITLE></HEAD>
  398. <BODY>
  399. <H1>Mail address not allowed</H1>
  400. <P>The mail address you managed to submit, <B>$destaddr</B>, to this script is
  401. not one of the pre-defined set of addresses that are allowed.  Go back and
  402. try again.</P>
  403. </BODY></HTML>
  404. EOH
  405.     ;
  406.         exit(0);
  407.     }
  408.     }
  409.  
  410.     # if we just received an alias, then convert that to an address
  411.     $realaddr = $destaddr;
  412.     if ($addrs{$destaddr}) {
  413.     $realaddr = "$destaddr <$addrs{$destaddr}>";
  414.     }
  415.  
  416.     # fork over the mail to sendmail and be done with it
  417.     if ($active) {
  418.     if ($listserver) {
  419.         open(MAIL,"| $sendmail$fromaddr") ||
  420.         &InternalError('Could not fork sendmail with -f switch');
  421.     }
  422.     else {
  423.         open(MAIL,"| $sendmail") ||
  424.         &InternalError('Could not fork sendmail with -f switch');
  425.     }
  426.  
  427.     # only print Cc if we got one
  428.     print MAIL "Cc: $cc\n" if $cc;
  429.     print MAIL <<EOM;
  430. From: $fromname <$fromaddr>
  431. To: $realaddr
  432. Reply-To: $replyto
  433. Errors-To: $errorsto
  434. Sender: $sender
  435. Subject: $subject
  436. X-Mail-Gateway: Doug\'s WWW Mail Gateway $version
  437. X-Real-Host-From: $realfrom
  438.  
  439. $body
  440.  
  441. $data
  442. EOM
  443.     close(MAIL);
  444.     }
  445.  
  446.     # give some short confirmation results
  447.     #
  448.     # if the cgi var 'nexturl' is given, give out the location, and let
  449.     # the browser do the work.
  450.     if ($nexturl) {
  451.     print "Location: $nexturl\n\n";
  452.     }
  453.     # otherwise, give them the standard form.
  454.     else {
  455.     print &PrintHeader();
  456.     print <<EOH;
  457. <HTML><HEAD><TITLE>Mailto results</TITLE></HEAD>
  458. <BODY><H1>Mailto results</H1>
  459. <P>Mail sent to <B>$destaddr</B>:<BR><BR></P>
  460. <PRE>
  461. <B>Subject</B>: $subject
  462. <B>From</B>: $fromname <$fromaddr>
  463.  
  464. $body</PRE>
  465. <HR>
  466. <A HREF="$script_http">Back to the WWW Mailto Gateway</A>
  467. </BODY></HTML>
  468. EOH
  469.     ;
  470.     }
  471. }                # end if METHOD=POST
  472.  
  473. #####################################
  474. # What the heck are we doing here????
  475. #####################################
  476.  
  477. else {
  478.     print <<EOH;
  479. <HTML><HEAD><TITLE>Mailto Gateway error</TITLE></HEAD>
  480. <BODY><H1>Mailto Gateway error</H1>
  481. <P>Somehow your browser generated a non POST/GET request method and it
  482. got here.  You should get this fixed!!</P></BODY></HTML>
  483. EOH
  484. }
  485.  
  486. exit(0);
  487.  
  488.  
  489. #
  490. # Deal out error messages to the user.  Gets passed a string containing
  491. # a description of the error
  492. #
  493. sub InternalError {
  494.     local($errmsg) = @_;
  495.  
  496.     print &PrintHeader();
  497.     print <<EOH;
  498. Content-type: text/html
  499. Status: 502 Bad Gateway
  500.  
  501. <HTML><HEAD><TITLE>Mailto Gateway Internal Error</TITLE></HEAD>
  502. <BODY><H1>Mailto Gateway Internal Error</H1>
  503. <P>Your mail failed to send for the following reason:<BR><BR>
  504. <B>$errmesg</B></P></BODY></HTML>
  505. EOH
  506.     exit(0);
  507. }
  508.  
  509.  
  510. ##
  511. ## end of mailto.pl
  512. ##
  513.